﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Entity.Interface;
using RayDen.Library.Entity.Scene;
using RayDen.RayEngine;
using RayDen.RayEngine.Core.Interface;
using RayDen.RayEngine.Core.Types;
using RayDen.RayEngine.Data;
using RayDen.RayEngine.Samplers;

namespace RayDen.MegaRay.Core.Fabrics
{
    public class VertexFactory
    {
        private readonly EngineContext context;
        private readonly TextureSamplingManager texSampler;


        public VertexFactory(EngineContext ctx)
        {
            context = ctx;
            this.texSampler = new TextureSamplingManager(ctx.Scene.TextureSamplingQuality);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public PathVertexData CreateVertex(ref RayRecord ray, ref RayHit rayHit)
        {
            var wo = -ray.Direction;

            if (rayHit.Miss())
            {
                return new PathVertexData()
                    {
                        Flags = (byte)PathVertexType.Environment,
                        Emission = context.Scene.SampleEnvironment(ref wo)
                    };
            }
            var vertex = new PathVertexData { HitPoint = ray.Point(rayHit.Distance) };

            var mesh = context.Scene.GetMeshByTriangleIndex((int)rayHit.Index);

            if (mesh.MeshProfile is SurfaceProfile)
            {
                vertex.Flags = (byte)PathVertexType.Surface;
                vertex.SurfaceHit = new UV(rayHit.U, rayHit.V);
                var meshMat = mesh.MaterialID;
                var MMaterial = context.Scene.MatLib.GetSurfMat(meshMat);
                var matInfo = context.Scene.MaterialProvider.Get(meshMat);
                mesh.InterpolateTriangleNormal((int)rayHit.Index, rayHit.U, rayHit.V, ref vertex.GeoNormal);
                var rayDir = ray.Direction;
                vertex.ShadingNormal = (Normal.Dot(ref rayDir, ref vertex.GeoNormal) > 0f) ? -vertex.GeoNormal : vertex.GeoNormal;
                UV texCoord;
                mesh.InterpolateTriUV((int)rayHit.Index, rayHit.U, rayHit.V, out texCoord);
                vertex.Brdf = MMaterial;
                vertex.MaterialInfo = matInfo;
                var diffuseTex = context.Scene.Query(meshMat, TextureType.Diffuse);
                if (diffuseTex != null)
                {
                    texSampler.SampleTexture(texCoord.U, texCoord.V, diffuseTex, out vertex.Color);
                    //ii.Color = diffuseTex.Sample(ii.TexCoords.U, ii.TexCoords.V);
                    //ii.Color = diffuseTex.Sample(hit.U, hit.V);
                }
                else
                {
                    vertex.Color = RgbSpectrum.Max(ref matInfo.Ks, ref matInfo.Kd);
                }
            }
            else if (mesh.MeshProfile is LightsourceProfile)
            {
                vertex.Flags = (byte)PathVertexType.Light;
                var light = context.Scene.GetLightByIndex((int)rayHit.Index);
                vertex.LightId = context.Scene.Lights.ToList().IndexOf(light);
                vertex.Emission = (RgbSpectrum)(light.Le(ref wo));
            }
            else if (mesh.MeshProfile is VolumeProfile)
            {
                vertex.Flags = (byte)PathVertexType.Volume;
            }

            return vertex;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public PathVertexData CreateEyeVertex(ICamera camera, Sample sample)
        {
            IRay ray;
            camera.GetRay(sample.imageX, sample.imageY, out ray);
            return new PathVertexData()
                {
                    Flags = (byte)PathVertexType.Camera,
                    CameraPoint = ray.Origin,
                    CameraDirection = ray.Direction,
                    Lens = new UV(sample.GetLazyValue(), sample.GetLazyValue()),
                    Time = sample.GetLazyValue()
                };
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public PathVertexData CreateLightVertex(Sample Sample)
        {
            LightSample ls;
            var light = context.Scene.Lights[context.Scene.SampleLights(Sample.GetLazyValue())];
            light.EvaluatePhoton(context.Scene, Sample.GetLazyValue(), Sample.GetLazyValue(), Sample.GetLazyValue(), Sample.GetLazyValue(), Sample.GetLazyValue(), out ls);
            var ray = ls.LightRay;
            var weight = ls.Pdf * (1f / context.Scene.Lights.Length);
            var thr = (RgbSpectrum)(ls.Spectrum) * weight;
            return new PathVertexData
                {
                    Flags = (byte)PathVertexType.Light,
                    Emission = thr,
                    HitPoint = ray.Org,
                    OutgoingDirection = ray.Dir,
                    
                };
        }

    }
}
